package conf

import (
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/viper"
	"log"
	"os"
	"path"
	"path/filepath"
	"wf-deploy-agent/util"
)

var (
	CF = parse()
)

type Config struct {
	Agent     *AgentConfig     `mapstructure:"agent"`
	Task      *TaskConfig      `mapstructure:"task"`
	Registry  *RegistryConfig  `mapstructure:"registry"`
	Kafka     *KafkaConfig     `mapstructure:"kafka"`
	Scheduler *SchedulerConfig `mapstructure:"scheduler"`
	Logging   *LoggingConfig   `mapstructure:"logging"`
	Plugin    *PluginConfig    `mapstructure:"plugin"`
}

type Identity struct {
	Id       int64  `mapstructure:"WF_AGENT_ID"`
	Identity string `mapstructure:"WF_AGENT_IDENTITY"`
}

type AgentConfig struct {
	Id         int64
	Identity   string
	Version    string
	Service    *Service
	Directory  *Directory
	Repository *Repository
}

type Service struct {
	Name string
	Host string
	Port int32
	Env  string
}

type Repository struct {
	Name   string
	Enable bool
}

type Directory struct {
	Base   string
	File   string
	Backup string
	Plugin string
}

type TaskConfig struct {
	Sync *SyncTaskConfig
}

type SyncTaskConfig struct {
	Plugin bool
}

type RegistryConfig struct {
	Root string
	Type string
	Host []string
	Auth *Auth
}

type Auth struct {
	Username string
	Password string
}

type KafkaConfig struct {
	Brokers           []string
	Auth              *Auth
	ConsumeAttempts   uint   `json:"consume-attempts" yaml:"consume-attempts"`
	ClusterTopic      string `json:"cluster-topic" yaml:"cluster-topic"`
	IssueTopic        string `json:"issue-topic" yaml:"issue-topic"`
	IssueNodeTopic    string `json:"issue-node-topic" yaml:"issue-node-topic"`
	IssueItemTopic    string `json:"issue-item-topic" yaml:"issue-item-topic"`
	ReleaseTopic      string `json:"release-topic" yaml:"release-topic"`
	ReleaseNodeTopic  string `json:"release-node-topic" yaml:"release-node-topic"`
	ReleaseStageTopic string `json:"release-stage-topic" yaml:"release-stage-topic"`
}

type SchedulerConfig struct {
	Name string `json:"name"`
}

type LoggingConfig struct {
	File       string
	Level      string
	MaxAge     int
	MaxSize    int
	MaxBackups int
	Compress   bool
}

type PluginConfig struct {
	Loader *Loader
}

type Loader struct {
	Speed int
}

func setViperDefault() {
	viper.SetDefault("Agent.Service.Name", "wf-deploy-agent")
	viper.SetDefault("Agent.Service.Port", "10881")
	viper.SetDefault("Agent.Service.Env", "prod")
	viper.SetDefault("Agent.Directory.Base", "/data/wf-deploy-agent")
	viper.SetDefault("Agent.Directory.File", "file")
	viper.SetDefault("Agent.Directory.Backup", "backup")
	viper.SetDefault("Agent.Directory.Plugin", "plugin")
	viper.SetDefault("Agent.Repository.Name", "wf-deploy-repo")
	viper.SetDefault("Agent.Repository.Enable", "true")
	viper.SetDefault("Task.Sync.Plugin", "true")
	viper.SetDefault("Registry.Root", "/services")
	viper.SetDefault("Registry.Host", "127.0.0.1:2181")
	viper.SetDefault("Kafka.Brokers", "127.0.0.1:9092")
	viper.SetDefault("Kafka.ConsumeAttempts", "3")
	viper.SetDefault("Kafka.ClusterTopic", "cluster")
	viper.SetDefault("Kafka.IssueTopic", "issue")
	viper.SetDefault("Kafka.IssueNodeTopic", "issue-node")
	viper.SetDefault("Kafka.IssueItemTopic", "issue-item")
	viper.SetDefault("Kafka.ReleaseTopic", "release")
	viper.SetDefault("Kafka.ReleaseNodeTopic", "release-node")
	viper.SetDefault("Kafka.ReleaseStageTopic", "release-stage")
	viper.SetDefault("Scheduler.Name", "wf-deploy-scheduler")
	viper.SetDefault("Logging.File", "wf-deploy-agent.log")
	viper.SetDefault("Logging.Level", "info")
	viper.SetDefault("Logging.MaxAge", 10)
	viper.SetDefault("Logging.MaxSize", 100)
	viper.SetDefault("Logging.MaxBackups", 10)
	viper.SetDefault("Logging.Compress", false)
	viper.SetDefault("Plugin.Loader.Speed", "10")
}

func parse() *Config {
	setViperDefault()
	viper.SetConfigName("agent")
	//viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		log.Fatalln(err)
	}

	cf := &Config{}

	var tagName string
	ext := filepath.Ext(viper.ConfigFileUsed())
	if len(ext) > 1 {
		tagName = ext[1:]
	}

	decoderConfigOption := func(c *mapstructure.DecoderConfig) {
		c.TagName = tagName
	}

	err := viper.Unmarshal(&cf, decoderConfigOption)
	if err != nil {
		log.Fatalln(err)
	}

	if cf.Agent.Service.Host == "" {
		cf.Agent.Service.Host = util.GetLocalIp()
	}

	cfg := getIdentity()

	cf.Agent.Id = cfg.Id
	cf.Agent.Identity = cfg.Identity
	cf.Agent.Version = "V2.1.1.0"

	return cf
}

func getIdentity() *Identity {
	viper.SetConfigName("identity")
	viper.SetConfigType("env")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalln(err)
	}

	cfg := Identity{}
	err = viper.Unmarshal(&cfg)
	if err != nil {
		log.Fatalln(err)
	}

	if cfg.Id <= 0 {
		fmt.Println("WF_AGENT_ID environment variable has illegal value!")
		fmt.Println("Press the enter key to exit!")
		_, _ = fmt.Scanln()
		os.Exit(0)
	}

	if cfg.Identity == "" {
		fmt.Println("WF_AGENT_IDENTITY environment variable not found or has no value!")
		fmt.Println("Press the enter key to exit!")
		_, _ = fmt.Scanln()
		os.Exit(0)
	}

	return &cfg
}

func GetBaseDir() string {
	d := CF.Agent.Directory
	return filepath.Clean(d.Base)
}

func GetFileDir() string {
	d := *CF.Agent.Directory
	return filepath.Clean(path.Join(d.Base, d.File))
}

func GetBackupDir() string {
	d := *CF.Agent.Directory
	return filepath.Clean(path.Join(d.Base, d.Backup))
}

func GetPluginDir() string {
	d := *CF.Agent.Directory
	return filepath.Clean(path.Join(d.Base, d.Plugin))
}

func GetInstanceHost() string {
	return fmt.Sprintf("%s:%d", CF.Agent.Service.Host, CF.Agent.Service.Port)
}
